package com.example.springboot.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Constructor;

/**
 * InstantiationAwareBeanPostProcessor的子接口, 颗粒度更细，所以在InstantiationAwareBeanPostProcessor前执行
 * <p> 用于对bean类型及构造的选择
 * <p>执行顺序
 *  <pre>
 *   SmartInstantiationAwareBeanPostProcessor.predictBeanType
 *    ↓
 *   SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors
 *    ↓
 *   InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
 *    ↓
 *   实例化 (构造方法)
 *    ↓
 *   InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
 *    ↓
 *   Aware 接口回调
 *    ↓
 *   InstantiationAwareBeanPostProcessor.postProcessPropertyValues
 *    ↓
 *   SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference
 *    ↓
 *   BeanPostProcessor.postProcessBeforeInitialization
 *    ↓
 *   PostConstruct / InitializingBean.afterPropertiesSet
 *    ↓
 *   BeanPostProcessor.postProcessAfterInitialization
 *
 *  </pre>
 * <p>
 * @author Zhang.Z
 * @version 2025-09-21
 *
 * @see Test5InstantiationAwareBeanPostProcessor
 * @see Test9InitializingBean
 * @see Test6MyAware
 */
@Configuration
public class Test4SmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    /**
     * 该触发点发生在postProcessBeforeInstantiation之前,
     *  这个方法用于预测推断Bean的类型，返回第一个预测成功的Class类型，如果不能预测返回null；
     *  当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息
     */
    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        if ("car".equals(beanName)){

            System.out.println("test4 predict bean type :" + beanClass);
        }
        return beanClass;
    }

    /**
     * 决定用哪个构造函数来实例化 Bean
     * <p>该触发点发生在postProcessBeforeInstantiation之后，用于确定该bean的构造函数之用，返回的是该bean的所有构造函数列表。
     * 用户可以扩展这个点，来自定义选择相应的构造器来实例化这个bean
     */
    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        if ("car".equals(beanName)){
            System.out.println("test4 determine Constructor");
        }
        return null;
    }

    /**
     * 在 Bean 初始化完成前获取“早期引用”，主要解决 循环依赖 问题
     * <p>该触发点发生在postProcessAfterInstantiation之后，
     * 当有循环依赖的场景，当bean实例化好之后，为了防止有循环依赖，会提前暴露回调方法，用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        if ("car".equals(beanName)){
            System.out.println("test4 early bean reference");
        }
        return bean;
    }
}
